//@author A0088044A
#include <string>
#include <time.h>
#include "Storage.h"

using namespace std;

#define  XML_STR_CAT_INFO       "Task-info" 
#define  XML_STR_FILE           "PowerSchedule.xml" 
#define  XML_STR_ROOT           "xml-root"
#define  RET_OK                 0   
#define  RET_ERR                -1   

#define  XML_RETURN(x)          { if( RET_OK != (x) ) return RET_ERR; }   
#define  XML_ASSERT(x)          { if( NULL == (x) ) return RET_ERR; }   



Storage::Storage(void) : logger(Logger::getInstance()) {
	ReadXmlFromFile();
}



Storage::~Storage(void)
{
	WriteXmlToFile();
}


string Storage::addTask(Task newTask)
{
	totalNumberOfTasks ++;
	int numDigits=0;
	int temp = totalNumberOfTasks/10;
	while (temp>0)
	{
		temp = temp/10;
		numDigits++;
	}
	stringstream ss;
	ss << "task";
	for (int i = 0; i<6-numDigits; i++)
		ss << "0";
	ss << totalNumberOfTasks;
	newTask.setID(ss.str());
	list.push_back(newTask);
	WriteXmlToFile();
	return ss.str();
}

//retrieve task by id
vector<Task>  Storage::search(string keyword) // Argument is keyword
{
	vector<Task> searchResult;
	vector<Task>::iterator iter;
	Utilities::toLowerCase(keyword);

	for( iter = list.begin(); iter != list.end(); ++iter )
	{
		if ((*iter).getStatus() != TASK_DELETED)
		{
			string description = (*iter).getDescription();
			string tag = (*iter).getTag();
			Utilities::toLowerCase(description);
			Utilities::toLowerCase(tag);

			if (description.find(keyword) != string::npos || tag.find(keyword) != string::npos){
				searchResult.push_back(*iter);
			}
		}
	}

	return searchResult;
}

//retrieve target by id
Task  Storage::retrieve(string id) // Argument is keyword
{
	vector<Task>::iterator iter;
	for( iter = list.begin(); iter != list.end(); ++iter )
	{
		if((*iter).getID()==id)
		{
			return (*iter);
		}
	}

	return Task();
}


//display all data
vector<Task> Storage::displayToday()
{
	time_t t = time(0); 
	char tmp[64]; 
	strftime( tmp, sizeof(tmp), "%d%m%Y",localtime(&t) );  

	string today(tmp);

	vector<Task> display;
	vector<Task>::iterator iter;


	for (unsigned i=0; i<list.size(); i++)
	{
		if((list[i].getStatus() != TASK_DELETED) && (list[i].getDate() == today))
		{
			display.push_back(list[i]);
		}
	}

	return display;
}

//get the task by id and update
void  Storage::modify(Task t)
{
	vector<Task>::iterator iter;
	for( iter = list.begin(); iter != list.end(); ++iter )
	{
		if((*iter).getID()==t.getID())
		{
			(*iter) = t;
		}
	}
	WriteXmlToFile();
}

void Storage::setStatus(string taskID, TaskStatus newStatus)
{
	//vector<Task>::iterator iter;
	//for( iter = list.begin(); iter != list.end(); ++iter )
	for (unsigned i=0; i < list.size(); i++) 	
	{
		if(list[i].getID()== taskID)
		{
			list[i].setStatus(newStatus);
		}
	}
	WriteXmlToFile();
}

void Storage::undone(string taskID)
{
	vector<Task>::iterator iter;
	for( iter = list.begin(); iter != list.end(); ++iter )
	{
		if((*iter).getID()== taskID)
		{
			(*iter).setStatus(TASK_ACTIVE);
		}
	}
	WriteXmlToFile();
}

//remove task by id
void  Storage::remove(string taskID) // Argument is the Task ID
{
	vector<Task>::iterator iter;
	for( iter = list.begin(); iter != list.end(); ++iter )
	{
		if((*iter).getID()==taskID)
		{
			//cout << (*iter).id << endl;
			(*iter).setStatus(TASK_DELETED); 
		}
	}
	WriteXmlToFile();
}

void Storage::removeLast()
{
	list.pop_back();
	WriteXmlToFile();
}

void Storage::unremove(string taskID)
{
	vector<Task>::iterator iter;
	for( iter = list.begin(); iter != list.end(); ++iter )
	{
		if((*iter).getID()==taskID)
		{
			//cout << (*iter).id << endl;
			(*iter).setStatus(TASK_ACTIVE); 
		}
	}
	WriteXmlToFile();
}

//add leaf node   
int AddLeafNode( TiXmlNode* pElmParent, const char* pszNode, const char* pszText )  
{  
	TiXmlElement elmNode(pszNode);  
	TiXmlText elmText(pszText);  
	XML_ASSERT( elmNode.InsertEndChild( elmText ) );  
	XML_ASSERT( pElmParent->InsertEndChild( elmNode ) );  
	return RET_OK;  
}  
//get leaf node   
int GetLeafNode( TiXmlNode* pElmParent, char* pszNode, char* pszText )  
{     
	TiXmlNode *pTemp;  
	if ( pElmParent && ( pTemp = pElmParent->FirstChild( pszNode ) ) )  
	{  
		if ( pTemp = pTemp->FirstChild() )  
		{  
			strcpy( pszText, pTemp->Value() );   
			return RET_OK;  
		}  
	}  
	return RET_ERR;  
}  

//write list task to xml
int  Storage::WriteXmlToFile( )  
{  
	TiXmlDeclaration Declaration( "1.0","", "" );  
	TiXmlDocument xmlDoc(XML_STR_FILE);   
	xmlDoc.InsertEndChild( Declaration );   
	TiXmlElement elmRoot( XML_STR_ROOT  );  
	char szBuf[32];  
	szBuf[0] = '\0';  
	if ( list.size()==0 )  
	{  
		return RET_ERR   ;  
	}   
	for ( unsigned i = 0; i < list.size(); i++ )  
	{  
		if (list[i].getStatus() != TASK_DELETED)
		{
			TiXmlElement elmCat( XML_STR_CAT_INFO );  

			XML_RETURN( AddLeafNode( &elmCat, "id", list[i].getID().c_str() ) );  

			sprintf( szBuf,"%s", list[i].getDate().c_str());  
			XML_RETURN( AddLeafNode( &elmCat, "date", szBuf ) ); 

			sprintf( szBuf,"%s", list[i].getStartTime().c_str());  
			XML_RETURN( AddLeafNode( &elmCat, "start_time", szBuf ) );  

			sprintf( szBuf,"%s", list[i].getEndTime().c_str());  
			XML_RETURN( AddLeafNode( &elmCat, "end_time", szBuf ) ); 

			XML_RETURN( AddLeafNode( &elmCat, "description", list[i].getDescription().c_str() ) );  

			XML_RETURN( AddLeafNode( &elmCat, "status", taskStatusToString(list[i].getStatus()).c_str())); 

			XML_RETURN( AddLeafNode( &elmCat, "tag", list[i].getTag().c_str() ) ); 

			XML_ASSERT( elmRoot.InsertEndChild( elmCat ) );
		}
	}  
	XML_ASSERT( xmlDoc.InsertEndChild( elmRoot ) );  
	//save   
	xmlDoc.SaveFile();  
	return RET_OK;  
}  
//read task list from xml
int Storage::ReadXmlFromFile()  
{  
	TiXmlElement *pRootNode = NULL;  
	TiXmlElement *pTemp = NULL;  
	char szBuf1[32];  
	szBuf1[0] = '\0';  
	char szBuf2[32];  
	szBuf2[0] = '\0';  
	char szBuf3[32];  
	szBuf3[0] = '\0';  
	char szBuf4[32];  
	szBuf4[0] = '\0'; 
	char szBuf5[32];  
	szBuf5[0] = '\0';
	char szBuf6[32];  
	szBuf6[0] = '\0';
	char szBuf7[32];  
	szBuf7[0] = '\0';

	totalNumberOfTasks = 0;
	TiXmlDocument xmlDoc( XML_STR_FILE );   
	if( !xmlDoc.LoadFile() )   
		return RET_ERR;  


	XML_ASSERT( pRootNode = xmlDoc.RootElement() );  

	TiXmlNode *pCat = NULL;  
	pCat = pRootNode->FirstChild( XML_STR_CAT_INFO ); 
	int i=0;
	while ( pCat )  
	{ 
		szBuf2[0] = '\0'; 
		szBuf3[0] = '\0'; 
		szBuf4[0] = '\0'; 
		szBuf5[0] = '\0'; 
		szBuf6[0] = '\0'; 
		szBuf7[0] = '\0'; 

		/*for ( int i = 0; i < pCats->iNum; i++ )  
		{*/  
		//Task li;

		//id   
		GetLeafNode( pCat, "id", szBuf1 );  
		//li.id=szBuf1;
		//date
		GetLeafNode( pCat, "date", szBuf2 );  
		// li.date = atoi(szBuf);  
		//time
		GetLeafNode( pCat, "start_time", szBuf3 );  
		GetLeafNode( pCat, "end_time", szBuf4 );  
		// li.time = atoi(szBuf);  
		//time   
		GetLeafNode( pCat, "description", szBuf5); 
		//li.description=szBuf;
		GetLeafNode( pCat, "status", szBuf6); 

		GetLeafNode( pCat, "tag", szBuf7); 

		Task li(szBuf5, szBuf2, szBuf3, szBuf4, szBuf7);

		li.setStatus(stringToTaskStat(szBuf6));
		li.setID(szBuf1);
		list.push_back(li);

		i++;

		pCat = pCat->NextSibling();  
		//}  
	} 

	if (szBuf1[0] == '\0') {
		totalNumberOfTasks = 0;
	}
	else {
		string tempString;
		for (int i = 0; i < 7; i++) {
			tempString += szBuf1[4+i];
		}
		totalNumberOfTasks = stoi(tempString);
	}
	/*}  
	else  
	{  
	return RET_ERR;  
	} */ 
	return RET_OK;  
}  

TaskStatus Storage::stringToTaskStat(string str)
{
	if (str == "TASK_ACTIVE")
	{
		return TASK_ACTIVE;
	}
	else if (str == "TASK_DONE")
	{
		return TASK_DONE;
	}
	else if (str == "TASK_ONGOING")
	{
		return TASK_ONGOING;
	}
	else if (str == "TASK_OVERDUE")
	{
		return TASK_OVERDUE;
	}

	return TASK_DELETED;
}


string Storage::taskStatusToString(TaskStatus taskStat)
{
	switch (taskStat) {
	case TASK_ACTIVE: return "TASK_ACTIVE";
	case TASK_DONE: return "TASK_DONE";
	case TASK_DELETED: return "TASK_DELETED";
	case TASK_ONGOING: return "TASK_ONGOING";
	case TASK_OVERDUE: return "TASK_OVERDUE";
	}

	return "";
}